<template>
  <div>
    <modal-view
      title="图片裁切"
      ref="modalView"
      :submit-loading="true"
      width="800px"
      submit-text="确定"
      @on-submit="onSubmit"
      @on-close="onClose"
    >
      <div class="page-body">
        <div class="page-main">
          <div class="page-img" ref="imgBox">
            <div
              class="page-clip-box"
              ref="clipBox"
              :style="{
                'width':clipBoxWpx+'px','height':clipBoxHpx+'px',
                'top':clipBoxTop+'px','left':clipBoxLeft+'px',
              }"
            >
              <!-- 这个是用于拖动的 -->
              <div class="page-clip-content"></div>

              <!-- 上边框 -->
              <div
                class="page-clip-box-line"
                style="left:0px;top:-5px;cursor:s-resize;width:100%"
                @mousedown="onClipAddLineResize('top',$event)"
              ></div>
              <!-- 下边框 -->
              <div
                class="page-clip-box-line"
                style="left:0px;bottom:-5px;cursor:s-resize;width:100%"
                @mousedown="onClipAddLineResize('bottom',$event)"
              ></div>
              <!-- 左边框 -->
              <div
                class="page-clip-box-line"
                style="left:-5px;top:0px;cursor:e-resize;height:100%"
                @mousedown="onClipAddLineResize('left',$event)"
              ></div>
              <!-- 右边框 -->
              <div
                class="page-clip-box-line"
                style="right:-5px;top:0px;cursor:e-resize;height:100%"
                @mousedown="onClipAddLineResize('right',$event)"
              ></div>
              <!-- 左上角 -->
              <div
                class="page-clip-box-block"
                style="left:-5px;top:-5px;cursor:nw-resize"
                @mousedown="onClipBlockMouseDown('rb',$event)"
              ></div>
              <!-- 左下角 -->
              <div
                class="page-clip-box-block"
                style="left:-5px;bottom:-5px;cursor:sw-resize"
                @mousedown="onClipBlockMouseDown('rt',$event)"
              ></div>
              <!-- 右上角 -->
              <div
                class="page-clip-box-block"
                style="right:-5px;top:-5px;cursor:ne-resize"
                @mousedown="onClipBlockMouseDown('lb',$event)"
              ></div>
              <!-- 右下角 -->
              <div
                class="page-clip-box-block"
                style="right:-5px;bottom:-5px;cursor:se-resize"
                @mousedown="onClipBlockMouseDown('lt',$event)"
              ></div>
            </div>
          </div>
        </div>
        <!-- 右侧数据 -->
        <div class="page-right">
          <div class="page-right-title">图片要求宽高比:</div>
          <div class="page-right-wh">宽({{clipBoxW}}):高({{clipBoxH}})</div>
          <div class="page-right-line">预览框</div>
          <div>
            <div
              class="page-img-show"
              :style="{
              'width':imgShowWidth+'px',
              'height':imgShowHeight+'px',
            }"
            >
              <img
                :src="imgUrl"
                :style="{'width':imgShowInWidth+'px',
                 'transform': `translate(${imgShowInLeft}px,${imgShowInTop}px)`}"
              />
            </div>

            <!-- <button @click="imgShowToCanvas">生成canvas</button> -->

            <div id="test-asd"></div>
          </div>
        </div>
      </div>
    </modal-view>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Watch } from "vue-property-decorator";
import ModalView from "@/components/ModalView";

@Component({
  components: {
    ModalView
  },
  filters: {}
})
export default class BannerManageModal extends Vue {
  //声明类型
  public $refs!: {
    modalView: ModalView;
    clipBox: HTMLElement;
    //用于放置需要裁切图片的盒子
    imgBox: HTMLElement;
    //预览框
    imgShowCanvas: HTMLCanvasElement;
  };

  /**
   *切图框的宽度比例
   */
  clipBoxW: number = 0;

  /**
   *切图框的高度比例
   */
  clipBoxH: number = 0;

  /**
   * 图片链接
   */
  imgUrl: string = "";

  /**
   * 图片的实际宽度
   */
  imgWidth: number = 0;

  /**
   * 图片的实际高度
   */
  imgHeight: number = 0;

  /**
   * 创建的时候
   */
  created() {}

  mounted() {}

  /**
   * 展示页面
   */
  public show(imgUrl: string, clipBoxW: number, clipBoxH: number) {
    this.imgUrl = imgUrl;
    this.clipBoxW = clipBoxW;
    this.clipBoxH = clipBoxH;
    this.$refs.modalView.show();
    this.$nextTick(() => {
      this.init();
    });
  }

  /**
   * 页面保存
   */
  onSubmit(para: { success: any; fail: any }) {
    //生成Blob
    this.imgShowToCanvas()
      .then(e => {
        para.success();
        this.$emit("on-submit", e);
      })
      .catch(() => {
        this.$message.warning("裁切图片生成错误,请重试");
        para.fail();
      });
  }

  /**
   * 页面关闭
   */
  onClose() {
    this.$emit("on-close");
  }

  /**
   * 切割功能加载
   */
  init() {
    //切割框
    let clipBox = this.$refs.clipBox;
    //首先需要加载图片
    let imgDom = document.createElement("img");
    imgDom.src = this.imgUrl;
    imgDom.style.width = "100%";
    imgDom.style.height = "100%";
    imgDom.style.display = "block";
    //将图片添加到页面中
    this.$refs.imgBox.appendChild(imgDom);
    //图片加载完成
    imgDom.onload = e => {
      this.imgWidth = imgDom.offsetWidth;
      this.imgHeight = imgDom.offsetHeight;
      //根据图片计算裁切框的实际尺寸和位置
      this.resetClipBox();
      //计算展示框中 背景图片的长度
      this.resetImgShowInWidth();
      //计算展示框中背景图片的位置
      this.resetImgShowInResize();
    };

    //为切割框添加拖动效果
    this.clipAddDrag();

    //创建一个预览框
    this.createImgShow();
    //为切割框添加角调整功能
    // this.clipAddBlockResize();
  }

  /**
   * 等比缩放
   */
  resetSize(
    // 固定角
    fixed: "lt" | "lb" | "rt" | "rb",
    // fixed: "top" | "bottom" | "left" | "right",
    //缩放对应的 宽 -高(表示 缩放数量对应的是宽 还是i高)
    positionWH: "width" | "height",
    //缩放数量
    cout: number,
    //这些数据 是表示 赋值一个用于计算的默认值
    //此时的cout 就不是根据实际位置计算 , 而是根据赋予的 默认位置计算
    { cacheTop = 0, cacheLeft = 0, cacheWidth = 0, cacheHeight = 0 } = {}
  ) {
    let top: number = cacheTop || this.clipBoxTop;
    let left: number = cacheLeft || this.clipBoxLeft;
    let width: number = cacheWidth || this.clipBoxWpx;
    let height: number = cacheHeight || this.clipBoxHpx;

    // 更新宽高
    if (positionWH == "width") {
      //缩放为宽
      width += cout;
      height += (cout * this.clipBoxH) / this.clipBoxW;
    } else {
      //缩放为宽
      height += cout;
      width += (cout * this.clipBoxW) / this.clipBoxH;
    }

    // 更新位置
    switch (fixed) {
      //固定左上角
      case "lt": {
        break;
      }
      //固定左下角
      case "lb": {
        if (positionWH == "width") {
          top = cacheTop - (height - cacheHeight);
        } else {
          top -= cout;
        }
        break;
      }
      //固定右上角
      case "rt": {
        if (positionWH == "width") {
          left -= cout;
        } else {
          left = cacheLeft - (width - cacheWidth);
        }
        break;
      }
      //固定右下角
      case "rb": {
        if (positionWH == "width") {
          top = cacheTop - (height - cacheHeight);
          left -= cout;
        } else {
          left = cacheLeft - (width - cacheWidth);
          top -= cout;
        }
        break;
      }
    }

    //添加边界判断
    if (top < 0) return;
    if (left < 0) return;
    if (left + width > this.imgWidth) return;
    if (top + height > this.imgHeight) return;
    //赋值
    this.clipBoxTop = top;
    this.clipBoxLeft = left;
    this.clipBoxWpx = width;
    this.clipBoxHpx = height;
  }

  /**
   * 鼠标拖拽委托
   */
  mouseDragEvent(
    /**
     * 需要委托的dom元素
     */
    eventDom: HTMLElement,
    {
      /**
       *鼠标按下去
       */
      onDown = (e: MouseEvent) => {},
      /**
       * 当鼠标移动的时候
       */
      onMove = (e: MouseEvent) => {},
      /**
       * 当鼠标放开的时候
       */
      onUp = (e: MouseEvent) => {}
    }
  ) {
    /**
     * 当鼠标按下去的时候
     */
    eventDom.onmousedown = function(e) {
      onDown(e);
      document.onmousemove = function(e) {
        onMove(e);
      };
      document.onmouseup = function(e) {
        onUp(e);
        //置空鼠标拖动  , 和鼠标抬起的事件
        document.onmousemove = null;
        document.onmouseup = null;
      };
    };
  }

  /**
   * ================================
   *  切割框功能
   * ================================
   */

  /**
   * 切图框的实际宽度
   */
  clipBoxWpx: number = 0;

  /**
   * 切图框的实际高度
   */
  clipBoxHpx: number = 0;

  /**
   * 切图框的实际top
   */
  clipBoxTop: number = 0;

  /**
   * 切图框的实际left
   */
  clipBoxLeft: number = 0;

  /**
   * 根据图片计算裁切框的实际尺寸和位置
   */
  resetClipBox() {
    //计算出实际的裁切框的长宽来
    let k = this.imgWidth / this.imgHeight - this.clipBoxW / this.clipBoxH;
    if (k > 0) {
      this.clipBoxHpx = this.imgHeight;
      this.clipBoxWpx = (this.imgHeight / this.clipBoxH) * this.clipBoxW;
    } else if (k < 0) {
      this.clipBoxWpx = this.imgWidth;
      this.clipBoxHpx = (this.imgWidth / this.clipBoxW) * this.clipBoxH;
    } else {
      this.clipBoxWpx = this.imgWidth;
      this.clipBoxHpx = this.imgHeight;
    }
    //计算位置 , 应该在实际图片的中间
    this.clipBoxLeft = (this.imgWidth - this.clipBoxWpx) / 2;
    this.clipBoxTop = (this.imgHeight - this.clipBoxHpx) / 2;
  }

  /**
   * 为切割框添加拖动效果
   */
  clipAddDrag() {
    //为切割框添加拖动效果
    //添加拖拽效果
    let clipBox = this.$refs.clipBox;
    let clipDrag = clipBox.querySelector(".page-clip-content");

    if (clipDrag instanceof HTMLElement) {
      //偏移量
      let disX: number = 0;
      let disY: number = 0;
      //为切割框添加拖动
      this.mouseDragEvent(clipDrag, {
        onDown(e) {
          // 鼠标按下，计算当前元素距离可视区的距离
          //这里面需要计算外边距的偏移量;
          disX = e.clientX - clipBox.offsetLeft;
          disY = e.clientY - clipBox.offsetTop;
        },
        onMove: e => {
          let top = e.clientY - disY;
          let left = e.clientX - disX;
          //添加边界判断
          if (top <= 0) {
            this.clipBoxTop = 0;
          } else if (top + this.clipBoxHpx <= this.imgHeight) {
            this.clipBoxTop = top;
          } else {
            this.clipBoxTop = this.imgHeight - this.clipBoxHpx;
          }
          if (left <= 0) {
            this.clipBoxLeft = 0;
          } else if (left + this.clipBoxWpx <= this.imgWidth) {
            this.clipBoxLeft = left;
          } else {
            this.clipBoxLeft = this.imgWidth - this.clipBoxWpx;
          }
        },
        onUp: e => {
          //计算展示框中背景图片的位置
          this.resetImgShowInResize();
        }
      });
    }
  }

  /**
   * 为切割框添加边框 调整功能
   */
  onClipAddLineResize(
    position: "top" | "bottom" | "left" | "right",
    e: MouseEvent
  ) {
    // 添加边框调整功能
    // console.log(clipLineList);

    let cacheTop: number = this.clipBoxTop;
    let cacheLeft: number = this.clipBoxLeft;
    let cacheWidth: number = this.clipBoxWpx;
    let cacheHeight: number = this.clipBoxHpx;

    //记录下鼠标当前的位置
    let disY: number = e.clientY;
    let disX: number = e.clientX;

    /**
     * 固定角
     */
    let fixed: "rt" | "lt" | "lb" | "rb" = "lt";
    if (position == "bottom" || position == "right") {
      fixed = "lt";
    } else if (position == "top") {
      fixed = "lb";
    } else if (position == "left") {
      fixed = "rt";
    }

    /**
     * 缩放的依据
     */
    let positionWH: "width" | "height" = "width";
    if (position == "top" || position == "bottom") {
      positionWH = "height";
    } else {
      positionWH = "width";
    }

    document.onmousemove = e => {
      /**
       *  缩放距离
       */
      let count: number = 0;
      switch (position) {
        case "top": {
          count = -(e.clientY - disY);
          break;
        }
        case "bottom": {
          count = e.clientY - disY;
          break;
        }
        case "left": {
          count = -(e.clientX - disX);
          break;
        }
        case "right": {
          count = e.clientX - disX;
          break;
        }
      }

      //进行等比缩放
      this.resetSize(fixed, positionWH, count, {
        cacheLeft,
        cacheTop,
        cacheHeight,
        cacheWidth
      });
      // 获取鼠标当前偏移量
      // disY = e.clientY;
    };
    document.onmouseup = e => {
      document.onmousemove = null;
      document.onmouseup = null;
      //计算展示框中背景图片宽度
      this.resetImgShowInWidth();
      //计算展示框中背景图片的位置
      this.resetImgShowInResize();
    };
  }

  /**
   * 当点击切割框角的时候
   */
  onClipBlockMouseDown(fixed: "lt" | "lb" | "rt" | "rb", e: MouseEvent) {
    let cacheTop: number = this.clipBoxTop;
    let cacheLeft: number = this.clipBoxLeft;
    let cacheWidth: number = this.clipBoxWpx;
    let cacheHeight: number = this.clipBoxHpx;

    //处理操作顶部边框 和 底部边框
    //记录下鼠标当前的位置
    let disY: number = e.clientY;
    let disX: number = e.clientX;
    document.onmousemove = e => {
      /**
       * 缩放距离
       */
      let count = 0;
      /**
       * 缩放的依据
       */
      let positionWH: "width" | "height" = "width";

      let x = 0;
      let y = 0;

      switch (fixed) {
        //固定左上角
        case "lt": {
          x = e.clientX - disX;
          y = e.clientY - disY;
          break;
        }
        //固定左下角
        case "lb": {
          x = e.clientX - disX;
          y = -(e.clientY - disY);
          break;
        }
        //固定右上角
        case "rt": {
          x = -(e.clientX - disX);
          y = e.clientY - disY;
          break;
        }
        //固定右下
        case "rb": {
          x = -(e.clientX - disX);
          y = -(e.clientY - disY);
          break;
        }
      }

      if (Math.abs(x / this.clipBoxW) > Math.abs(y / this.clipBoxH)) {
        count = x;
        positionWH = "width";
      } else {
        count = y;
        positionWH = "height";
      }

      // if (fixed != "lt") {
      //   count = -count;
      // }
      // console.log(x, y);
      // console.log(position);

      //进行等比缩放
      this.resetSize(fixed, positionWH, count, {
        cacheLeft,
        cacheTop,
        cacheHeight,
        cacheWidth
      });
    };
    document.onmouseup = e => {
      document.onmousemove = null;
      document.onmouseup = null;
      //计算展示框中背景图片宽度
      this.resetImgShowInWidth();
      //计算展示框中背景图片的位置
      this.resetImgShowInResize();
    };
  }

  /**
   * 为切割框添加角 调整功能
   */
  clipAddBlockResize() {
    // 获取全部的角
    let clipBox = this.$refs.clipBox;
    let clipBlockList = clipBox.querySelectorAll(".page-clip-box-block");
    for (let item of clipBlockList) {
      if (item instanceof HTMLElement) {
        // console.log(item.dataset["position"]);
      }
    }
  }

  /**
   * ================================
   *  预览框数据
   * ================================
   */

  /**
   * 预览框的宽度
   */
  imgShowWidth: number = 250;

  /**
   * 预览框的高度
   */
  imgShowHeight: number = 0;

  /**
   * 预览框中背景图片应有的长度
   */
  imgShowInWidth: number = 0;

  /**
   * 预览框中背景图片的top
   */
  imgShowInTop: number = 0;

  /**
   * 预览框中背景图片的left
   */
  imgShowInLeft: number = 0;

  /**
   * 背景图片img 的dom
   */
  imgDom: any;

  /**
   * 创建预览框canvas
   */
  createImgShow() {
    this.imgShowHeight = (this.imgShowWidth / this.clipBoxW) * this.clipBoxH;
  }

  /**
   * 计算展示框中 背景图片应有的长度
   */
  resetImgShowInWidth() {
    this.imgShowInWidth = (this.imgShowWidth * this.imgWidth) / this.clipBoxWpx;
  }

  /**
   * 计算展示框中 背景图片的位置
   */
  resetImgShowInResize() {
    //计算比例
    let a = this.clipBoxWpx / this.imgShowWidth;
    this.imgShowInTop = -(this.clipBoxTop / a);
    this.imgShowInLeft = -(this.clipBoxLeft / a);
  }

  /**
   * 将图片转化为canvas
   */
  imgShowToCanvas(): Promise<File> {
    let canvas = document.createElement("canvas");
    //为了保证图片清晰度没有问题 , 我们需要等比放大
    let k = 2;
    canvas.width = this.imgShowWidth * k;
    canvas.height = this.imgShowHeight * k;
    let ctx = canvas.getContext("2d");
    let img = document.createElement("img");
    img.src = this.imgUrl;
    this.imgDom = img;
    return new Promise((resolve, reject) => {
      img.onload = () => {
        if (ctx instanceof CanvasRenderingContext2D) {
          ctx.drawImage(
            img,
            this.imgShowInLeft * k,
            this.imgShowInTop * k,
            this.imgShowInWidth * k,
            ((this.imgShowInWidth * this.imgHeight) / this.imgWidth) * k
          );
          //canvas 转化为base64
          canvas.toBlob(e => {
            if (e instanceof Blob) {
              resolve(new File([e], "1.png"));
            } else {
              reject(e);
            }
          });
        }
      };
    });
  }
}
</script>

<style lang="less" scoped>
.page-body {
  display: flex;
}
.page-main {
  flex-shrink: 0;
  user-select: none;
  @gzColor: rgb(231, 231, 231);
  width: 500px;
  height: 500px;
  // border: 1px solid rgba(0, 0, 0, 0.1);
  background-image: linear-gradient(
      45deg,
      @gzColor 25%,
      transparent 25%,
      transparent 75%,
      @gzColor 75%,
      @gzColor
    ),
    linear-gradient(
      45deg,
      @gzColor 26%,
      transparent 26%,
      transparent 74%,
      @gzColor 74%,
      @gzColor
    );
  background-size: 30px 30px;
  background-position: 0 0, 15px 15px;
  display: flex;
  justify-content: center;
  align-items: center;
}
.page-img {
  max-width: 100%;
  max-height: 100%;
  position: relative;
  overflow: hidden;
  > img {
    width: 100%;
    height: 100%;
    display: block;
  }
}

.page-clip-box {
  position: absolute;
  // box-sizing: content-box;
  // user-select: 500 red;
  border: 1px solid rgba(255, 255, 255, 0.5);
  box-shadow: 0 0 500px 500px rgba(0, 0, 0, 0.5);
}
// 用于拖动
.page-clip-content {
  width: 100%;
  height: 100%;
  cursor: move;
}
// 角块
.page-clip-box-block {
  width: 9px;
  height: 9px;
  border: 1px solid #e3e4e5;
  background-color: rgba(0, 0, 0, 0.3);
  position: absolute;
  // z-index: -1;
}
// 边框
.page-clip-box-line {
  position: absolute;
  width: 9px;
  height: 9px;
  // background-color: red;
}

.page-right {
  flex-basis: 0;
  flex-grow: 1;
  margin-left: 20px;
}
.page-right-title {
  font-size: 15px;
  margin-bottom: 5px;
}
.page-right-wh {
  font-size: 18px;
  font-weight: bold;
}
.page-right-line {
  margin: 15px 0 5px 0;
  font-size: 13px;
}
.page-img-show {
  border: 1px solid rgba(0, 0, 0, 0.1);
  overflow: hidden;
}
</style>
